为了维护和管理 ChatMessage ,LangChain4j 提供了 ChatMemory 抽象以及多种开箱即用的实现,。ChatMemory 既可以作为独立底层组件使用,也可以作为高级组件(如 AI Services)的一部分。
ChatMemory 充当 ChatMessage 的容器(底层由 List 支持),并附带以下功能:
- 淘汰策略(Eviction Policy)
- 持久化(Persistence)
- 对 SystemMessage 的特殊处理
- 对工具消息的特殊处理
记忆 vs 历史
“记忆(memory)”和”历史(history)”是相似但不同的概念。
⚠️ LangChain4j 目前仅提供”记忆”功能,不提供”历史”功能。如果您需要保留完整历史,请手动实现。
- 历史(History):完整保留用户与 AI 之间的所有消息。历史是用户在 UI 中看到的内容,代表实际发生的对话。
- 记忆(Memory):保留部分信息,将其呈现给 LLM,使 LLM 表现得好像”记住了”对话。记忆与历史截然不同。
根据所使用的记忆算法,它可以以多种方式修改历史:淘汰部分消息、汇总多条消息、汇总单独的消息、从消息中移除不重要的细节、向消息中注入额外信息(如用于 RAG)或指令(如用于结构化输出)等。
淘汰策略(Eviction Policy)
淘汰策略的存在有以下几点原因:
- 适应 LLM 的上下文窗口:LLM 一次能处理的 token 数量有上限。当对话超过这个限制时,需要淘汰部分消息。通常淘汰最早的消息,但也可以根据需要实现更复杂的算法。
- 控制成本:每个 token 都有成本,发送给 LLM 的 token 越多,每次调用的费用就越高。淘汰不必要的消息可以降低成本。
- 控制延迟:发送给 LLM 的 token 越多,处理所需的时间就越长。
两种开箱即用的实现
| 实现类 | 淘汰策略 | 适用场景 |
|---|---|---|
MessageWindowChatMemory |
按消息条数淘汰 | 快速原型开发 |
TokenWindowChatMemory |
按 Token 数量淘汰 | 需要精确控制 token 成本的场景 |
MessageWindowChatMemory
较为简单的一种,以滑动窗口方式运行,保留最近的 N 条消息,淘汰不再适合的旧消息。然而,由于每条消息可能包含不同数量的 token,MessageWindowChatMemory 主要用于快速原型设计。
TokenWindowChatMemory
更复杂的选项,同样以滑动窗口方式运行,但专注于保留最近的 N 个 token,根据需要淘汰旧消息。消息是不可分割的——如果一条消息放不下,它会被整体淘汰。TokenWindowChatMemory 需要一个 Tokenizer 来计算每条 ChatMessage 中的 token 数。
持久化(Persistence)
默认情况下,ChatMemory 实现将 ChatMessage 存储在内存中。如果需要持久化,可以实现自定义的 ChatMemoryStore,将 ChatMessage 存储在您选择的任何持久化存储中:
1 | class PersistentChatMemoryStore implements ChatMemoryStore { |
ChatMemoryStore 方法详解
updateMessages()
每次向 ChatMemory 添加新的 ChatMessage 时,都会调用 updateMessages() 方法。这通常在与 LLM 的每次交互中发生两次:一次是添加新的 UserMessage 时,另一次是添加新的 AiMessage 时。updateMessages() 方法预期会更新与给定 memory ID 关联的所有消息。ChatMessage 可以单独存储(例如每条消息一条记录/行/对象),也可以一起存储(例如整个 ChatMemory 一条记录/行/对象)。
⚠️ 请注意,从
ChatMemory中淘汰的消息也将从ChatMemoryStore中淘汰。当消息被淘汰时,会调用updateMessages()方法,传入不包含被淘汰消息的消息列表。
getMessages()
当 ChatMemory 的用户请求所有消息时,会调用 getMessages() 方法。这通常在与 LLM 的每次交互中发生一次。Object memoryId 参数的值对应于创建 ChatMemory 时指定的 id,可用于区分多个用户和/或对话。getMessages() 方法预期会返回与给定 memory ID 关联的所有消息。
deleteMessages()
当调用 ChatMemory.clear() 时,会调用 deleteMessages() 方法。如果您不使用此功能,可以将此方法留空。
SystemMessage 的特殊处理
SystemMessage 是一种特殊类型的消息,因此与其他消息类型处理方式不同:
SystemMessage一旦添加就会一直保留,不会被淘汰策略自动移除。- 每个
ChatMemory中只能保留一个SystemMessage。 - 添加相同的
SystemMessage会被忽略,添加不同的SystemMessage会保留最新的那个。
工具消息的特殊处理
如果包含 ToolExecutionRequest 的 AiMessage 被淘汰,后续孤立的 ToolExecutionResultMessage 也会自动被淘汰,以避免某些 LLM 提供商(如 OpenAI)出现问题——它们禁止在请求中发送孤立的 ToolExecutionResultMessage。